import { IAssemblyConstraint } from '@amcax/base';
import { MessageTip } from '@app-base/common/utils/MessageTip';
import { PRODUCT_EVENT } from '@app-cad/common/Constants';
import { useMemoizedFn } from 'ahooks';
import i18n from 'i18next';
import { useContext, useEffect, useMemo, useRef, useState } from 'react';
import { PickableCommandForm } from '../../../../common/components/command-form/pickable';
import { useCommand } from '../../../../common/hooks/useCommand';
import { useTools } from '../../../../common/hooks/useTools';
import { DocumentContext } from '../../../../common/providers/documentProvider';
import ReverseIconWidget from '../../../../common/RJSFWidgets/ReverseIconWidget';
import { getIdOfPickedObj } from '../../../../common/utils/render';
import { ctx2, userEvents } from '../../../../module';
import { FeaturedCmdDialogParams } from '../../../../types';
import { getAllConstraints } from '../../utils';
import { ConstraintType } from '../cmd-constrain/type';
import { useAssemblyPreview } from '../common/AssemblyPreviewAnimator';
import ConstraintArrayFieldItemTemplate from './Custom-Form-Components/ConstraintArrayFieldItemTemplate';
import ConstraintArrayFieldTemplate from './Custom-Form-Components/ConstraintArrayFieldTemplate';
import ConstraintArrayFieldTitleTemplate from './Custom-Form-Components/ConstraintArrayFieldTitleTemplate';
import ConstraintArraySchemaField from './Custom-Form-Components/ConstraintArraySchemaField';
import { extraUiSchema, schema } from './form-definition';
import './index.scss';
import { CopyConstraintRefType, FormData } from './types';

function CopyWithConstraints({
  title,
  onClose,
  placement,
}: FeaturedCmdDialogParams) {
  const initData: FormData = {
    select: null,
    constraints: [],
  };

  const { docTree } = useContext(DocumentContext);
  const [formData, setFormData] = useState<FormData>(initData);
  const constraintsRef = useRef<CopyConstraintRefType[]>([]);

  const { pickerAssembly } = useTools();

  useEffect(() => {
    if (formData.select) {
      constraintsRef.current =
        getAllConstraints(docTree)
          ?.map((node) => ({
            ...(node.originalObj as IAssemblyConstraint),
            label: node.title,
          }))
          .filter(
            ({ item, target, type }) =>
              (item.startsWith(formData.select.productId) ||
                target.startsWith(formData.select.productId)) &&
              type !== ConstraintType.Fix,
          )
          .map(({ id, label, item, target, type, val, vecSense }) => ({
            id,
            title: label,
            target: item.startsWith(formData.select.productId) ? target : item,
            type,
            val,
            vecSense,
            used: true,
            reuse: false,
            reverse: false,
          })) || [];

      if (constraintsRef.current.length > 0) {
        MessageTip.destroy();

        initEditDependency(
          constraintsRef.current.map(({ target }) => target),
        ).then(() =>
          setFormData((prevData) => ({
            ...prevData,
            constraints: constraintsRef.current.map(({ target, ...rest }) => ({
              target: pickerAssembly.generatePickObjByPickID(target),
              ...rest,
            })),
          })),
        );
      } else {
        MessageTip.error('不可复制选定零部件,因为它没有可复制的配合。');
        setFormData((prevData) => ({
          ...prevData,
          constraints: [],
        }));
      }
    } else {
      constraintsRef.current = [];
      setFormData((prevData) => ({
        ...prevData,
        constraints: [],
      }));
    }
  }, [formData.select, docTree]);

  useEffect(() => {
    let hasChanges = false;

    if (!constraintsRef.current.length && formData.constraints.length) {
      setFormData((prevData) => ({
        ...prevData,
        constraints: [],
      }));
      return;
    }

    const updatedConstraints = formData.constraints.map((constraint) => {
      const { used, reuse, target } = constraint;
      if (
        used &&
        !reuse &&
        getIdOfPickedObj(target)?.startsWith(formData.select.productId)
      ) {
        MessageTip.error('不允许选择同一零件的参考几何图形。');

        hasChanges = true;
        return {
          ...constraint,
          target: null,
        };
      }

      if (!used && constraint.target) {
        hasChanges = true;
        return {
          ...constraint,
          reuse: false,
          target: null,
          reverse: false,
        };
      }

      if (used && reuse) {
        const savedConstraint = constraintsRef.current.find(
          (saved) => saved.id === constraint.id,
        );
        if (savedConstraint.target !== getIdOfPickedObj(target)) {
          hasChanges = true;
          return {
            ...constraint,
            target: pickerAssembly.generatePickObjByPickID(
              savedConstraint.target,
            ),
          };
        }
      }

      return constraint;
    });

    if (hasChanges) {
      setFormData((prevData) => ({
        ...prevData,
        constraints: updatedConstraints,
      }));
    }
  }, [formData.constraints]);

  const isFormDataValid = () => {
    const { select, constraints } = formData;

    if (select) {
      const usedConstraints = constraints.filter(
        (constraint) => constraint.used,
      );
      return (
        usedConstraints.length > 0 &&
        usedConstraints.every(({ target }) => !!target)
      );
    } else {
      return false;
    }
  };

  const finishValid = useMemo(isFormDataValid, [formData]);

  const execute = async (preview: boolean = false) => {
    const { select, constraints } = formData;
    try {
      return await ctx2.commandCenters.assemblyCommands.copyWithConstraint(
        select.productId,
        constraints
          .filter(({ used }) => used)
          .map(({ id, target, reverse }) => {
            return {
              id,
              target: getIdOfPickedObj(target),
              reverse,
            };
          }),
        preview,
      );
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
    } catch (err: any) {
      MessageTip.error(err.message);
    }
  };

  const checkAndFinish = async (preview = false) => {
    if (!isFormDataValid()) {
      return undefined;
    }

    const executionResult = await execute(preview);
    if (preview) {
      return executionResult;
    }
    userEvents.emit(PRODUCT_EVENT.UPDATE_CONSTRAINTS);

    onExit();
    return undefined;
  };

  const fetchPreview = useMemoizedFn(() => checkAndFinish(true));

  useAssemblyPreview(fetchPreview, formData);

  const onExit = () => {
    onClose();
  };

  const { initEditDependency } = useCommand('CopyWithConstraint', {
    onClose: onExit,
    finishDraw: checkAndFinish,
  });

  return (
    <PickableCommandForm
      isFormDataValid={finishValid}
      disabled={false}
      onClose={onExit}
      title={title}
      placement={placement}
      templates={{
        ArrayFieldTemplate: ConstraintArrayFieldTemplate,
        ArrayFieldTitleTemplate: ConstraintArrayFieldTitleTemplate,
        ArrayFieldItemTemplate: ConstraintArrayFieldItemTemplate,
      }}
      fields={{
        ArraySchemaField: ConstraintArraySchemaField,
      }}
      widgets={{
        reverseIcon: ReverseIconWidget,
      }}
      schema={schema}
      formData={formData}
      onChange={(v) => {
        if (v.select !== formData.select) {
          setFormData({ select: v.select, constraints: [] });
        } else {
          setFormData(v);
        }
      }}
      extraUiSchema={extraUiSchema}
      submitText='完成'
      onSubmit={() => checkAndFinish()}
    />
  );
}

export const cmdCopyWithConstraint = {
  id: 'CmdCopyWithConstraint',
  label: 'command.copy-with-constraints',
  icon: 'commands/product/copy-with-constraints',
  create: (label: string, onClose: () => void) => (
    <CopyWithConstraints
      title={i18n.t(label)}
      onClose={onClose}
    />
  ),
};
